/**
 * @brief  A non-lock circular buffer. 
 *
 * @author Huang RuiGuang
 * @date 2008/12/1
 *
 * Herm library
 * Copyright (c) 2008 by Huang RuiGuang(daemon.huang@gmail.com)
 *
 * Permission to use, copy, modify, distribute and sell this software.
 */

#ifndef HERM_BASE_BUFFER_H
#define HERM_BASE_BUFFER_H

#include <cstring>
#include <mutex>
#include "herm/export_symbols.h"

namespace herm{

class HERM_API Buffer
{
public:
	static const unsigned int MIN_SPACE_SIZE = 1;

	Buffer(unsigned int size);
	~Buffer();

	void Clear();
	/**
     * @brief Read data but don't move the m_read
	 */
	unsigned int Peek(char* buff, unsigned int len);
	bool SeekReadPtr(unsigned int len);

	/**
     * @brief Fetch data to buff.
	 * @return -1, failed; other value is the size of read.
	 */
	unsigned int Fetch(char* buff, unsigned int len);
	/**
	 * @brief Read a block data.
	 * @return, -1, invalid message; other value, read size.
	 */
	unsigned int FetchChunk(char* buff, unsigned int len);
	bool Push(const char* buff, unsigned int len);
	void PushBlock(const char* buff, unsigned int len);
	
	inline unsigned int Length() const
	{
		return m_write - m_read;
	}

	inline bool Empty() const
	{
		return Length() == 0;
	}

	inline bool Full() const
	{
		return m_size - Length() == 0;
	}

	inline unsigned int Space() const
	{
		return m_size - Length();
	}
protected:
	bool IsPowerOf2(unsigned int x);

#ifndef WIN32
	inline unsigned int min(unsigned int a, unsigned int b)
	{
		return a < b ? a : b;
	}
#endif

protected:
	char* m_buff;	
	unsigned int m_size;	
	unsigned int m_write;  // data is added at offset (in % size)
	unsigned int m_read; // data is extracted from off. (out % size)
};

class HERM_API SyncBuffer
{
public:
	SyncBuffer(unsigned int size);
	~SyncBuffer();

	/**
     * @brief Read data but don't move the m_read
	 */
	unsigned int Peek(char* buff, unsigned int len);
	bool SeekReadPtr(unsigned int len);
	unsigned int Fetch(char* buff, unsigned int len);
	/**
     * @brief Read a integrated message from object to \a buff.
	 * @return true, read \a len data, and the \a len is 0;
	 *         false, the message length is invalid.
	 */
	unsigned FetchChunk(char* buff, unsigned int len);
	bool Push(const char* buff, unsigned int len);

	inline void Clear()
	{
		std::lock_guard<std::mutex> guard(m_mutex);
		m_buffer->Clear();
	}

	inline unsigned int Length() 
	{
		std::lock_guard<std::mutex> guard(m_mutex);
		return m_buffer->Length();
	}

	inline bool Empty() 
	{
		std::lock_guard<std::mutex> guard(m_mutex);
		return m_buffer->Empty();
	}

	inline unsigned int Space() 
	{
		std::lock_guard<std::mutex> guard(m_mutex);
		return m_buffer->Space();
	}
private:
	inline bool IsPowerOf2(unsigned int x)
	{
		return (x != 0) && ((x & (x - 1)) == 0);
	}

	inline unsigned int min2(unsigned int a, unsigned int b)
	{
		return a < b ? a : b;
	}
private:
	Buffer* m_buffer;
	std::mutex m_mutex;
};
}

#endif
